#include "Thermostat.h"
#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ST7565.h>
#include <GLCD_ST7565.h>
#include "utility/font_helvB12.h"
#include "utility/font_4x6.h"

Thermostat::Thermostat()
:
ONE_WIRE_BUS(5), oneWire(ONE_WIRE_BUS),sensors(&oneWire),
LDR_PIN(4),GREEN_LED_PIN(6),RED_LED_PIN(9), ENTER_PIN(15),UP_PIN(16),DOWN_PIN(19),
fast_update_interval(100),slow_update_interval(2000),
//this forces both updates to run initially
last_fast_update(-fast_update_interval),last_slow_update(-slow_update_interval),
hour(0),minute(0),second(0),ms(0),
mode(NORMAL), mode_reset_delay(4000),
programHour(0)
{

  for(int i=0;i<24;i++)
  {
    program[i]=72.0;
  }
  //Serial.begin(9600);
  //Serial.println("Thermostat");
  //Serial.println("Thermostat 1");

  GLCD_ST7565::begin(0x19);

  //Serial.println("Thermostat 2a");
  //GLCD_ST7565::backLight(200);
  //Serial.println("Thermostat 3a");

  //Serial.println("Thermostat 4a");
  //Serial.println("Thermostat 5a");
  pinMode(GREEN_LED_PIN, OUTPUT);
  pinMode(RED_LED_PIN, OUTPUT);
  //Serial.println("Thermostat 6");
  pinMode(UP_PIN, INPUT); 
  pinMode(DOWN_PIN, INPUT); 
  pinMode(ENTER_PIN, INPUT); 
  digitalWrite(UP_PIN, HIGH);
  digitalWrite(DOWN_PIN, HIGH);
  digitalWrite(ENTER_PIN, HIGH);
  //Serial.println("Thermostat 7");
  sensors.begin();
  //Serial.println("Thermostat 8");
}

void Thermostat::update(){
  //Serial.println("Thermostat::update");
  int now=millis();
  //handle millis rolling over
  if (now<this->last_fast_update)
  {
    last_fast_update=last_slow_update=now;
  }
  fastUpdate(now);
  slowUpdate(now);
}

void Thermostat::fastUpdate(int now)
{
  //Serial.println("Thermostat::fastUpddate 1");
  if ((now-last_fast_update)>fast_update_interval)
  {
    //this->millis+=(now-last_fast_update);
    addMillis(now-last_fast_update);
    //Serial.println("Thermostat::fastUpddate 2");
    last_fast_update = now;

    int LDR = analogRead(LDR_PIN);                     // Read the LDR Value so we can work out the light level in the room.
    int LDRbacklight = map(LDR, 0, 1023, 50, 250);    // Map the data from the LDR from 0-1023 (Max seen 1000) to var GLCDbrightness min/max
    LDRbacklight = constrain(LDRbacklight, 0, 255);   // Constrain the value to make sure its a PWM value 0-255
    GLCD_ST7565::backLight(LDRbacklight);

    if(!enter_button && digitalRead(ENTER_PIN))
    {
      enter_button=true;
      last_button_push=now;
      switch (mode)
      {
      case NORMAL:
        mode=SET_HOUR;
        break;
      case SET_HOUR:
        mode=SET_MINUTE;
        break;
      case SET_MINUTE:
        mode=SELECT_HOUR;
        break;
      case SELECT_HOUR:
        mode=SET_TEMP;
        break;
      case SET_TEMP:
        mode=NORMAL;
        break;
      }
    }
    else if(!digitalRead(ENTER_PIN))
    {
      enter_button=false;
    }

    if(!up_button && digitalRead(UP_PIN))
    {
      up_button=true;
      last_button_push=now;
      if(mode==NORMAL)
      {
        program[getHour()]++;
      }
      else if(mode==SET_HOUR  )
      {
        addHours(1);
      }
      else if(mode==SET_MINUTE)
      {
        addMinutes(1);
      }
      else if (mode==SELECT_HOUR)
      {
        //TODO
        programHour++;
      }
      else if (mode==SET_TEMP)
      {
        program[programHour]+=1;
      }
    }
    else if (!digitalRead(UP_PIN))
    {
      up_button=false;
    }
    if(!down_button && digitalRead(DOWN_PIN))
    {
      down_button=true;
      last_button_push=now;
    }
    else if(!digitalRead(DOWN_PIN))
    {
      down_button=false;
    } 

    if(!enter_button && !up_button && !down_button && now-last_button_push>mode_reset_delay)
    {
      //if no button is pushed, and we've waited for the delay
      //and the current mode is to program the temperature
      if(mode==SET_TEMP){
        //only go back one mode
        mode=SELECT_HOUR;
        //and set the dealy
        last_button_push=now;
      }
      else{
        mode=NORMAL;
      }
    }
  }
}

void Thermostat::slowUpdate(int now)
{
  //Serial.println("Thermostat::slowUpdate 2");
  if ((now-last_slow_update)>slow_update_interval)
  {
    //Serial.println("Thermostat::slowUpdate 2");
    last_slow_update = now;

    sensors.requestTemperatures();
    this->temperature = (sensors.getTempFByIndex(0));

    //if (temp > maxtemp) maxtemp = temp;
    //if (temp < mintemp) mintemp = temp;

    //emonglcd.temperature = (int) (temp * 100);                          // set emonglcd payload
    //rf12_sendNow(0, &emonglcd, sizeof emonglcd);                     //send temperature data via RFM12B using new rf12_sendNow wrapper -glynhudson
    //rf12_sendWait(2);    
  }
}

void Thermostat::drawTemperature(){

  char cTemperature[6]={
    0};
  // GLCD Temperature
  GLCD_ST7565::setFont(font_helvB12);
  dtostrf(getTemperatureF(),0,2,cTemperature);
  strcat(cTemperature,"F");
  GLCD_ST7565::drawString(0,0,cTemperature);
}

void Thermostat::drawTime(){
  // Time
  GLCD_ST7565::setFont(font_helvB12);
  //HOUR(4)
  char cTime[11]={
    0 };
  strcat(cTime,"");
  char cHour[3]={
    0};
  int hour=getHour();
  itoa((int)hour,cHour,10);
  if  (hour<10) {
    strcat(cTime,"0");
  }
  strcat(cTime,cHour);
  strcat(cTime,": ");

  //MINUTE(4)  
  char cMinute[3]={
    0};
  int minute=getMinute();
  itoa(minute,cMinute,10);
  if  (minute<10) {
    strcat(cTime,"0");
  }
  strcat(cTime,cMinute);
  strcat(cTime,": ");

  //SECONDS(2)
  char cSecond[3]={
    0};
  itoa((int)getSecond(),cSecond,10);
  if  (getSecond()<10) {
    strcat(cTime,"0");
  }
  strcat(cTime,cSecond);

  GLCD_ST7565::drawString(0,15,cTime);
}

void Thermostat::drawLeds(){
  if(up_button){
    digitalWrite(RED_LED_PIN,HIGH);
  }
  else
  {
    digitalWrite(RED_LED_PIN,LOW);
  }
  if(enter_button){
    digitalWrite(GREEN_LED_PIN,HIGH);
  }
  else
  {    
    digitalWrite(GREEN_LED_PIN,LOW);
  }
}

void Thermostat::drawMode(){
  //MODE
  GLCD_ST7565::setFont(font_4x6);
  GLCD_ST7565::drawString(111,0,"MODE");//111 ok,112 too high
  switch(mode){
  case NORMAL:
    GLCD_ST7565::drawString(102,7,"Normal");//104 to high
    break;
  case SET_HOUR:
    GLCD_ST7565::drawString(95,7,"Set Hour");//96 too high
    break;
  case SET_MINUTE:
    GLCD_ST7565::drawString(87,7,"Set Minute");//88 too high
    break;
  case SELECT_HOUR:
    GLCD_ST7565::drawString(83,7,"Select Hour");//
    break;
  case SET_TEMP:
    GLCD_ST7565::drawString(95,7,"Set Temp");//
    break;
  }
}

void Thermostat::drawProgrammedTemperature(){

  //programmed temperature
  char pTemp[6]={
    0                                  };//9 is too low,10 works
  dtostrf(program[getProgramHour()],0,2,pTemp);
  GLCD_ST7565::drawString(105,33,pTemp);//108 is too high,105 is ok
}

int Thermostat::getProgramHour()
{
  if(mode==SET_TEMP || mode==SELECT_HOUR){
    return this->programHour;
  }
  else  {
    return getHour();
  }
}

void Thermostat::drawProgramTime(){
  //////////////////////////////
  //TODO program time
  int programHour=getProgramHour();
  char pTime[11]={
    0  };
  if(programHour<10)
  {
    strcat(pTime,"0");
  }
  char cHour[3]={
    0                            };
  itoa(programHour,cHour,10);
  strcat(pTime,cHour);
  //char cSecond[3]={
  //  0            };
  //itoa((int)getSecond(),cSecond,10);
  strcat(pTime,":00-");
  if(programHour<9 || programHour==23)
  {
    strcat(pTime,"0");
  }
  //hour+1, account for rolling over midnight
  itoa(programHour<23?programHour+1:0,cHour,10);

  if(programHour+1<9)
  {
    strcat(pTime,"0");
  }

  strcat(pTime,cHour);

  strcat(pTime,":00");

  GLCD_ST7565::drawString(0,39,pTime);
}

void Thermostat::draw()
{
  //Serial.println("Thermostat::draw");
  GLCD_ST7565::clear();
  GLCD_ST7565::fillRect(0,0,128,64,0);

  drawLeds();

  drawTemperature();
  drawTime();
  drawMode();
  drawProgrammedTemperature();
  drawProgramTime();

  GLCD_ST7565::refresh();
}

double Thermostat::getTemperatureF(){
  return this->temperature;
}

void Thermostat::addHours(int hs)
{
  this->hour+=hs;
  if(this->hour>=24)
  {
    this->hour%=24;
  }
}

void Thermostat::addMinutes(int ms)
{
  this->minute+=ms;
  if(this->minute>=60)
  {
    addHours(this->minute/60);
    this->minute%=60;
  }
}

void Thermostat::addSeconds(int ss)
{
  this->second+=ss;
  if(this->second>=60)
  {
    addMinutes(this->second/60);
    this->second%=60;
  }
}

void Thermostat::addMillis(int mss)
{
  this->ms+=mss;
  if(this->ms>=1000)
  {
    addSeconds(this->ms/1000);
    this->ms%=1000;
  }
}
int Thermostat::getHour()
{
  return this->hour;
}
int Thermostat::getMinute()
{
  return this->minute;
}
int Thermostat::getSecond()
{
  return this->second;
}










































